package top.chukongxiang.project.base.utils;

import io.netty.util.CharsetUtil;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

public class ByteUtil {
    public static byte getByte(byte[] memory, int index) {
        return memory[index];
    }
    public static Byte getByte(Byte[] memory, int index) {
        return memory[index];
    }

    public static short getShort(byte[] memory, int index) {
        return (short) (memory[index] << 8 | memory[index + 1] & 255);
    }

    public static Short getShort(Byte[] memory, int index) {
        if (memory[index]==null || memory[index+1]==null) return null;
        return (short) (memory[index] << 8 | memory[index + 1] & 255);
    }

    public static short getShortLE(byte[] memory, int index) {
        return (short) (memory[index] & 255 | memory[index + 1] << 8);
    }

    public static int getUnsignedMedium(byte[] memory, int index) {
        return (memory[index] & 255) << 16 | (memory[index + 1] & 255) << 8 | memory[index + 2] & 255;
    }

    public static int getUnsignedMediumLE(byte[] memory, int index) {
        return memory[index] & 255 | (memory[index + 1] & 255) << 8 | (memory[index + 2] & 255) << 16;
    }

    public static int getInt(byte[] memory, int index) {
        return (memory[index] & 255) << 24 | (memory[index + 1] & 255) << 16 | (memory[index + 2] & 255) << 8 | memory[index + 3] & 255;
    }

    public static int getIntLE(byte[] memory, int index) {
        return memory[index] & 255 | (memory[index + 1] & 255) << 8 | (memory[index + 2] & 255) << 16 | (memory[index + 3] & 255) << 24;
    }

    public static long getLong(byte[] memory, int index) {
        return ((long) memory[index] & 255L) << 56 | ((long) memory[index + 1] & 255L) << 48 | ((long) memory[index + 2] & 255L) << 40 | ((long) memory[index + 3] & 255L) << 32 | ((long) memory[index + 4] & 255L) << 24 | ((long) memory[index + 5] & 255L) << 16 | ((long) memory[index + 6] & 255L) << 8 | (long) memory[index + 7] & 255L;
    }

    public static long getLongLE(byte[] memory, int index) {
        return (long) memory[index] & 255L | ((long) memory[index + 1] & 255L) << 8 | ((long) memory[index + 2] & 255L) << 16 | ((long) memory[index + 3] & 255L) << 24 | ((long) memory[index + 4] & 255L) << 32 | ((long) memory[index + 5] & 255L) << 40 | ((long) memory[index + 6] & 255L) << 48 | ((long) memory[index + 7] & 255L) << 56;
    }

    public static void setByte(byte[] memory, int index, int value) {
        memory[index] = (byte) value;
    }

    public static void setShort(byte[] memory, int index, int value) {
        memory[index] = (byte) (value >>> 8);
        memory[index + 1] = (byte) value;
    }

    public static void setShortLE(byte[] memory, int index, int value) {
        memory[index] = (byte) value;
        memory[index + 1] = (byte) (value >>> 8);
    }

    public static void setMedium(byte[] memory, int index, int value) {
        memory[index] = (byte) (value >>> 16);
        memory[index + 1] = (byte) (value >>> 8);
        memory[index + 2] = (byte) value;
    }

    public static void setMediumLE(byte[] memory, int index, int value) {
        memory[index] = (byte) value;
        memory[index + 1] = (byte) (value >>> 8);
        memory[index + 2] = (byte) (value >>> 16);
    }

    public static void setInt(byte[] memory, int index, int value) {
        memory[index] = (byte) (value >>> 24);
        memory[index + 1] = (byte) (value >>> 16);
        memory[index + 2] = (byte) (value >>> 8);
        memory[index + 3] = (byte) value;
    }

    public static void setIntLE(byte[] memory, int index, int value) {
        memory[index] = (byte) value;
        memory[index + 1] = (byte) (value >>> 8);
        memory[index + 2] = (byte) (value >>> 16);
        memory[index + 3] = (byte) (value >>> 24);
    }

    public static void setLong(byte[] memory, int index, long value) {
        memory[index] = (byte) ((int) (value >>> 56));
        memory[index + 1] = (byte) ((int) (value >>> 48));
        memory[index + 2] = (byte) ((int) (value >>> 40));
        memory[index + 3] = (byte) ((int) (value >>> 32));
        memory[index + 4] = (byte) ((int) (value >>> 24));
        memory[index + 5] = (byte) ((int) (value >>> 16));
        memory[index + 6] = (byte) ((int) (value >>> 8));
        memory[index + 7] = (byte) ((int) value);
    }

    public static void setLongLE(byte[] memory, int index, long value) {
        memory[index] = (byte) ((int) value);
        memory[index + 1] = (byte) ((int) (value >>> 8));
        memory[index + 2] = (byte) ((int) (value >>> 16));
        memory[index + 3] = (byte) ((int) (value >>> 24));
        memory[index + 4] = (byte) ((int) (value >>> 32));
        memory[index + 5] = (byte) ((int) (value >>> 40));
        memory[index + 6] = (byte) ((int) (value >>> 48));
        memory[index + 7] = (byte) ((int) (value >>> 56));
    }


    public static String getString(byte[] memory,int index ,int length){
        return  new String(memory,index,length, CharsetUtil.UTF_8);
    }

    public static void setString(byte[] memory,int index,String value){
        byte[] bytes = value.getBytes(CharsetUtil.UTF_8);

        System.arraycopy(bytes,0,memory,index,bytes.length);
    }

    public static byte[] cut (byte[] src, int start, int len) {
        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            bytes[i] = src[i + start];
        }
        return bytes;
    }

    public static boolean haxByteEquals(int[] src, byte[] dst) {
        if (src.length!=dst.length) {
            return false;
        }

        for (int i = 0; i < src.length; i++) {
            if ((byte)src[i]!=dst[i]) {
                return false;
            }
        }
        return true;
    }

    public static boolean bytesBytesEquals(byte[] src, byte[] dst) {
        if (src.length!=dst.length) {
            return false;
        }
        if (src==dst) {
            return true;
        }
        for (int i = 0; i < src.length; i++) {
            if (src[i]!=dst[i]) {
                return false;
            }
        }
        return true;
    }

    public static boolean contains(byte[] src, byte b) {
        return indexOf(src, b) != -1;
    }
    public static boolean contains(byte[] src, byte[] bs) {
        if (src.length==0 || bs.length==0) return false;

        for (int i = 0; i < src.length - bs.length + 1; i++) {
            if (src[i]==bs[0]) {
                boolean yes = true;
                for (int j = 1; j < bs.length; j++) {
                    if (src[i+j] != bs[j]) {
                        yes = false;
                        break;
                    }
                }
                if (yes) {
                    return true;
                }
            }
        }
        return false;
    }

    public static int indexOf (byte[] src, byte b) {
        if (src==null || src.length==0) {
            return -1;
        }
        for (int i = 0; i < src.length; i++) {
            if (src[i]==b) {
                return i;
            }
        }
        return -1;
    }

    public static int indexOf(byte[] father, byte[] child){
        if (child.length==0 || child.length>father.length) { return -1; }
        for (int i = 0; i < father.length - child.length + 1; i++) {
            if (father[i] == child[0]) {
                int k = 1;
                for (; k < child.length; k++) {
                    if (child[k] != father[i + k]) { break; }
                }
                if (k == child.length) { return i; }
            }
        }
        return -1;
    }

    public static String byteArrayToHexStr(byte[] byteArray) {
        Formatter formatter = new Formatter();
        for (byte b : byteArray) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    public static String byteObjArrayToHexStr(Byte[] byteArray) {
        StringBuilder sb = new StringBuilder();
        for (Byte b : byteArray) {
            sb.append("0x");
            sb.append(b==null?"":byteToHexStr(b));
            sb.append(" ");
        }
        return sb.toString();
    }

    public static String byteArrayToHexStrNot0x(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(byteToHexStr(b));
        }
        return sb.toString();
    }

    public static String byteToHexStr(byte b) {
        int i = Byte.toUnsignedInt(b);
        return Integer.toHexString(i).toUpperCase();
    }

    public static List<Byte> parse(byte[] bytes) {
        List<Byte> list = new ArrayList<>();
        for (byte b : bytes) {
            list.add(b);
        }
        return list;
    }
    public static byte[] parse(Byte[] bytes) {
        byte[] bs = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            bs[i] = bytes[i];
        }
        return bs;
    }

    public static byte[] lengthSplit(byte[] bytes, int start, int length) {
        if (length<0) {
            throw new IllegalArgumentException("数组下标不能为负数: " + length);
        }
        byte[] rs = new byte[length];
        int j = 0;
        for (int i = start; i < start + length; i++) {
            rs[j] = bytes[i];
            j++;
        }
        return rs;
    }

    public static byte[] split(byte[] bytes, int start, int end){
        return lengthSplit(bytes, start, end - start);
    }

    public static byte[] split(byte[] bytes, int start) {
        return lengthSplit(bytes, start, bytes.length - start);
    }

    public static byte[] short2Byte(short s){
        byte[] b = new byte[2];
        for(int i = 0; i < 2; i++){

            //因为byte占4个字节，所以要计算偏移量
            int offset = 16 - (i+1)*8;

            //把16位分为2个8位进行分别存储
            b[i] = (byte)((s >> offset)&0xff);
        }
        return b;
    }


    /**
     * byte数组中取int数值，本方法适用于(低位在前，高位在后)的顺序，和和intToBytes（）配套使用
     *
     * @param src
     *            byte数组
     * @param offset
     *            从数组的第offset位开始
     * @return int数值
     */
    public static int bytesToInt(byte[] src, int offset) {
        int value;
        value = (int) ((src[offset] & 0xFF)
                | ((src[offset+1] & 0xFF)<<8)
                | ((src[offset+2] & 0xFF)<<16)
                | ((src[offset+3] & 0xFF)<<24));
        return value;
    }

    /**
     * byte数组中取int数值，本方法适用于(低位在后，高位在前)的顺序。和intToBytes2（）配套使用
     */
    public static int bytesToInt2(byte[] src, int offset) {
        int value;
        value = (int) ( ((src[offset] & 0xFF)<<24)
                |((src[offset+1] & 0xFF)<<16)
                |((src[offset+2] & 0xFF)<<8)
                |(src[offset+3] & 0xFF));
        return value;
    }

    public static Byte[] parseAsciiArr(byte[] asciiArr){
        if (asciiArr.length % 2 != 0) {
            return null;
        }
        Byte[] newBytes = new Byte[asciiArr.length/2];
        for (int i = 0; i < asciiArr.length; i+=2) {
            char c1 = (char) Byte.toUnsignedInt(asciiArr[i]);
            if ((c1<48 || (c1>57 && c1<65 ) || c1>70)) {
                newBytes[i/2] = null;
                continue;
            }
            char c2 = (char) Byte.toUnsignedInt(asciiArr[i+1]);
            if ((c2<48 || (c2>57 && c2<65 ) || c2>70)) {
                newBytes[i/2] = null;
                continue;
            }

            StringBuilder sb = new StringBuilder();
            sb.append(c1);
            sb.append(c2);
            String s = sb.toString();
            sb.setLength(0);
            sb = null;
            byte newByte = (byte) Integer.parseInt(s, 16);
            newBytes[i/2] = newByte;
        }
        return newBytes;
    }
    /**
     * 对象转byte
     * @param obj
     * @return
     */
    public static byte[] ObjectToByte(Object obj) {
        byte[] bytes = null;
        try {
            // object to bytearray
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(obj);

            bytes = bo.toByteArray();

            bo.close();
            oo.close();
        } catch (Exception e) {
            System.out.println("translation" + e.getMessage());
            e.printStackTrace();
        }
        return bytes;
    }

    /**
     * byte转对象
     * @param bytes
     * @return
     */
    public static Object ByteToObject(byte[] bytes) {
        Object obj = null;
        try {
            // bytearray to object
            ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
            ObjectInputStream oi = new ObjectInputStream(bi);

            obj = oi.readObject();
            bi.close();
            oi.close();
        } catch (Exception e) {
            System.out.println("translation" + e.getMessage());
            e.printStackTrace();
        }
        return obj;
    }

}
